package com.etop.weiway.wxmanage.service;

import com.etop.weiway.basic.entity.BaseEntity;
import com.etop.weiway.basic.service.BaseService;
import com.etop.weiway.commons.web.Pager;
import com.etop.weiway.wxmanage.dao.CustomReplyDAO;
import com.etop.weiway.wxmanage.dao.WeixinUserMessageDAO;
import com.etop.weiway.wxmanage.entity.*;
import com.etop.weiway.wxmanage.util.WeixinTimeUtil;
import com.etop.weixin.entity.customMsg.Articles;
import com.etop.weixin.entity.req.BaseReqEntity;
import com.etop.weixin.entity.req.msg.*;
import com.etop.weixin.service.CustomMsgService;
import com.etop.weixin.utils.CustomMsgUtil;
import com.etop.weixin.utils.MediaUtil;
import it.sauronsoftware.jave.*;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import it.sauronsoftware.jave.AudioAttributes;

import java.io.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * Created by KyLeo on 2015/1/21.
 */
@Service
public class WxUserMessageService extends BaseService {
    @Autowired
    private WeixinUserMessageDAO weixinUserMessageDAO;
    @Autowired
    private WxUserManageService wxUserManageService;
    @Autowired
    private CustomReplyDAO customReplyDAO;
    @Autowired
    private AccountService accountService;
    @Autowired
    private MaterialService materialService;
    //用户消息过期时间（默认是2天）
    private final static int MESSAGE_OUT_OF_DATE = 5 * 24 * 60 * 60;
    //用户聊天界面默认显示的最大聊天记录数
    private final static int MAX_CHAT_RECORD = 20;

    /**
     * 根据公众号ID来查询五天内的消息
     *
     * @param pager
     * @param accountId
     * @param keyword
     * @return
     * @throws Exception
     */
    public Pager<WeixinUserMessage> getWxMsgListByAccountId(Pager<WeixinUserMessage> pager,
                                                            Integer accountId, String keyword) throws Exception {
        log.debug("***************************根据公众号ID来查询五天内的消息******************************");
        Map<String, Object> params = createParamMap();
        params.put("accountId", accountId);
        String strQuery = "from WeixinUserMessage t where t.valid = 1 and t.account.id = :accountId";
        if (!StringUtils.isEmpty(keyword)) {
            strQuery += " and (t.content like :keyword or t.weixinUser.realname like :keyword or " +
                    "((t.weixinUser.realname is null or t.weixinUser.realname = :strEmpty) and t.weixinUser.nickname like :keyword))";
            params.put("keyword", getKeyWords(keyword));
            params.put("strEmpty", "");
        }
        strQuery += (" and t.createTime >= " + (new Date().getTime() / 1000 - MESSAGE_OUT_OF_DATE));
        strQuery += " order by t.createTime desc";
        List<WeixinUserMessage> list = weixinUserMessageDAO.find(strQuery, params, (pager.getPageNo() - 1) * pager.getPageSize(), pager.getPageSize());
        pager.setCount(weixinUserMessageDAO.getTotalCount(strQuery, params));
        pager.setList(list);
        return pager;
    }

    /**
     * 根据用户id和公众号id来查找对应的聊天记录
     *
     * @param user
     * @param accountId
     * @return
     * @throws Exception
     */
    public List<BaseEntity> getChatRecord(WeixinUser user, Integer accountId) throws Exception {
        log.debug("******************根据用户id和公众号id来查找对应的聊天记录**********************");
        Map<String, Object> msgParams = createParamMap();
        msgParams.put("accountId", accountId);
        msgParams.put("userId", user.getId());
        Map<String, Object> replyParams = createParamMap();
        replyParams.put("accountId", accountId);
        replyParams.put("openId", user.getOpenId());
        String strQueryMsg = "from WeixinUserMessage t where t.valid = 1 and t.account.id = :accountId and t.weixinUser.id = :userId";
        String strQueryReply = "from CustomReply t where t.valid = 1 and t.account.id = :accountId and t.openId = :openId";
        String strOrderBy = " order by t.createTime desc";
        strQueryMsg += strOrderBy;
        strQueryReply += strOrderBy;

        List<WeixinUserMessage> wlist = weixinUserMessageDAO.find(strQueryMsg, msgParams, 0, MAX_CHAT_RECORD);
        List<CustomReply> clist = customReplyDAO.find(strQueryReply, replyParams, 0, MAX_CHAT_RECORD);
        List<BaseEntity> list = new ArrayList<>();
        if (wlist != null && clist != null) {
            int num = 0, msgCursor = 0, replyCursor = 0;
            for (; num < MAX_CHAT_RECORD && msgCursor < wlist.size() && replyCursor < clist.size(); num++) {
                if (wlist.get(msgCursor).getCreateTime() * 1000 > clist.get(replyCursor).getCreateTime()) {
                    list.add(wlist.get(msgCursor));
                    msgCursor++;
                } else {
                    list.add(clist.get(replyCursor));
                    replyCursor++;
                }
            }
            if (num < MAX_CHAT_RECORD) {
                while (msgCursor < wlist.size()) {
                    list.add(wlist.get(msgCursor));
                    msgCursor++;
                }
                while (replyCursor < clist.size()) {
                    list.add(clist.get(replyCursor));
                    replyCursor++;
                }
            }
        }
        return list;
    }

    /**
     * 接收微信用户发送过来的消息，并且保存到本地数据库中
     *
     * @param baseReqEntity
     * @param accountId
     * @throws Exception
     */
    public void saveWxMsg(BaseReqEntity baseReqEntity, Integer accountId, String path) throws Exception {
        log.debug("*****************接收微信用户发送过来的消息，并且保存到本地数据库中**********************");
        WeixinUserMessage message = new WeixinUserMessage();
        Account account = new Account();
        account.setId(accountId);
        WeixinUser user = wxUserManageService.findWxUser(accountId, baseReqEntity.getFromUserName());
        message.setAccount(account);
        message.setWeixinUser(user);
        message.setUsername((user.getRealname() == null || "".equals(user.getRealname())) ? user.getNickname() : user.getRealname());
        message.setMsgType(baseReqEntity.getMsgType());
        message.setHaveReplied(0);
        message.setCreateTime(baseReqEntity.getCreateTime().longValue());
        message.setValid(new Short("1"));

        switch (baseReqEntity.getMsgType()) {
            case "text":
                TextMsgReq textMsgReq = (TextMsgReq) baseReqEntity;
                message.setMsgId(textMsgReq.getMsgId());
                message.setContent(textMsgReq.getContent());
                break;
            case "image":
                ImageMsgReq imageMsgReq = (ImageMsgReq) baseReqEntity;
                message.setMsgId(imageMsgReq.getMsgId());
                message.setMediaId(imageMsgReq.getMediaId());
                message.setContent(imageMsgReq.getPicUrl());
                break;
            case "voice":
                VoiceMsgReq voiceMsgReq = (VoiceMsgReq) baseReqEntity;
                message.setMsgId(voiceMsgReq.getMsgId());
                message.setMediaId(voiceMsgReq.getMediaId());
                message.setContent(voiceMsgReq.getFormat());

                this.saveMediaData(voiceMsgReq.getMediaId(), accountId, path + "home/wxmanage/temp/voice/", "mp3");
                break;
            case "video":
                VideoMsgReq videoMsgReq = (VideoMsgReq) baseReqEntity;
                message.setMsgId(videoMsgReq.getMsgId());
                message.setMediaId(videoMsgReq.getMediaId());
                message.setThumbMediaId(videoMsgReq.getThumbMediaId());
                message.setContent("mp4");
                this.saveMediaData(videoMsgReq.getMediaId(), accountId, path + "home/wxmanage/temp/video/", "mp4");
                break;
            case "location":
                LocationMsgReq locationMsgReq = (LocationMsgReq) baseReqEntity;
                message.setMsgId(locationMsgReq.getMsgId());
                String locationContent = "地理位置纬度为X，地理位置经度为Y，地图缩放大小为scale，地理位置信息为label";
                String content = locationContent.replaceAll("X", locationMsgReq.getLocation_X()).replaceAll("Y", locationMsgReq.getLocation_Y()).
                        replaceAll("scale", locationMsgReq.getScale()).replaceAll("label", locationMsgReq.getLabel());
                message.setContent(content);
                break;
            case "link":
                LinkMsgReq linkMsgReq = (LinkMsgReq) baseReqEntity;
                message.setMsgId(linkMsgReq.getMsgId());
                String linkContent = "消息标题为title，消息描述为description，消息链接url";
                String linkMsgContent = linkContent.replaceAll("title", linkMsgReq.getTitle()).replaceAll("description", linkMsgReq.getDescription()).
                        replaceAll("url", linkMsgReq.getUrl());
                message.setContent(linkMsgContent);
                break;
            default:
                log.info("*********************没有匹配到合适的消息类型 : " + baseReqEntity.getMsgType());
                break;
        }
        weixinUserMessageDAO.save(message);
    }

    /**
     * 根据多媒体id来下载并保存媒体数据
     *
     * @param mediaId
     * @param accountId
     * @param path
     * @param type
     * @return
     * @throws Exception
     */
    public void saveMediaData(String mediaId, Integer accountId, String path, String type) throws Exception {
        log.debug("**************************根据多媒体id来下载并保存媒体数据***************************");
        String accessToken = WeixinTimeUtil.getAccessToken(accountService.loadAccount(accountId));
        if ("mp3".equals(type)) {
            String source = path + mediaId + ".amr";
            String target = path + mediaId + ".mp3";
            MediaUtil.downloadMedia(accessToken, mediaId, path, "amr");

            File sourceFile = new File(source);
            try {
                AudioAttributes audio = new AudioAttributes();
                Encoder encoder = new Encoder();
                audio.setCodec("libmp3lame");
                EncodingAttributes attrs = new EncodingAttributes();
                attrs.setFormat("mp3");
                attrs.setAudioAttributes(audio);
                encoder.encode(sourceFile, new File(target), attrs);
            } catch (EncoderException ex) {
                //屏蔽掉无关紧要的异常
            } finally {
                if (sourceFile.exists()) {
                    sourceFile.delete();
                }
            }
        } else {
            MediaUtil.downloadMedia(accessToken, mediaId, path, type);
        }
    }

    /**
     * 把对应消息的已回复字段置1
     *
     * @param msgId
     */
    public void updateReply(String msgId) throws Exception {
        log.debug("****************把对应消息的已回复字段置1*******************");
        Map<String, Object> params = createParamMap();
        params.put("mid", Integer.parseInt(msgId));
        String strQuery = "update WeixinUserMessage t set t.haveReplied = 1 where t.valid = 1 and t.id = :mid";
        weixinUserMessageDAO.excute(strQuery, params);
    }

    /**
     * 根据id来删除用户消息
     *
     * @param msgId
     * @throws Exception
     */
    public void deleteUserMsgById(String msgId) throws Exception {
        log.debug("****************根据id来删除用户消息*******************");
        Map<String, Object> params = createParamMap();
        params.put("mid", Integer.parseInt(msgId));
        String strQuery = "update WeixinUserMessage t set t.valid = 0 where t.valid = 1 and t.id = :mid";
        weixinUserMessageDAO.excute(strQuery, params);
    }

    /**
     * 根据id来删除客服回复
     *
     * @param cid
     */
    public void deleteCustomReplyById(String cid) throws Exception {
        log.debug("********************根据id来删除客服回复*************************");
        Map<String, Object> params = createParamMap();
        params.put("cid", Integer.parseInt(cid));
        String strQuery = "update CustomReply t set t.valid = 0 where t.valid = 1 and t.id = :cid";
        customReplyDAO.excute(strQuery, params);
    }

    /**
     * 保存客服回复消息
     *
     * @param account
     * @param sendResult
     * @param openId
     * @param content
     * @param mid
     * @param type
     * @throws Exception
     */
    public void saveCustomReply(Account account, boolean sendResult, String openId, String content, String mid, String type,
                                Integer materialId, String materialName) throws Exception {
        log.debug("*****************************保存客服回复消息****************************");
        CustomReply reply = new CustomReply();
        WeixinUserMessage msg = null;
        if (mid != null) {
            msg = new WeixinUserMessage();
            msg.setId(Integer.parseInt(mid));
        }
        if (materialId != null && materialId > 0) {
            reply.setMaterialId(materialId);
        }
        if (materialName != null) {
            reply.setMaterialName(materialName);
        }
        reply.setAccount(account);
        reply.setOpenId(openId);
        reply.setReplyMessage(msg);
        reply.setMaterialType(type);
        reply.setContent(content);
        reply.setStatus(sendResult ? "发送成功" : "发送失败");
        reply.setCreateTime(new Date().getTime());
        reply.setValid(new Short("1"));

        customReplyDAO.save(reply);
    }

    /**
     * 发送文本消息给用户
     *
     * @param openId
     * @param content
     * @param mid
     * @param accountId
     * @throws Exception
     */
    public boolean saveAndPostTextMsg(String openId, String content, String mid, Integer accountId) throws Exception {
        log.debug("****************发送文本消息给用户*******************");
        Boolean sendResult = false;
        Account account = accountService.loadAccount(accountId);
        String accessToken = WeixinTimeUtil.getAccessToken(account);
        String jsonMsg = CustomMsgService.createTextMsg(openId, content);
        sendResult = CustomMsgUtil.sendCustomMsg(accessToken, jsonMsg);

        if (sendResult && mid != null) { //标记对应的用户消息为已回复
            this.updateReply(mid);
        }

        this.saveCustomReply(account, sendResult, openId, content, mid, "text", null, null);
        return sendResult;
    }

    /**
     * 发送图片消息给用户
     *
     * @param accountId
     * @param openId
     * @param materialId
     * @return
     * @throws Exception
     */
    public boolean saveAndPostImageMsg(Integer accountId, String openId, String materialId, String path) throws Exception {
        log.debug("*************************发送图片消息给用户*****************************");
        Boolean sendResult = false;
        Account account = accountService.loadAccount(accountId);
        String accessToken = WeixinTimeUtil.getAccessToken(account);
        MaterialImage image = materialService.loadMaterialImage(Integer.parseInt(materialId));
        String mediaId = WeixinTimeUtil.getImageMediaId(path + "home/wxmanage/upload/" +
                account.getUser().getId() + "/image/" + image.getUrl(), image);
        String jsonMsg = CustomMsgService.createImageMsg(openId, mediaId);
        sendResult = CustomMsgUtil.sendCustomMsg(accessToken, jsonMsg);
        this.saveCustomReply(account, sendResult, openId, image.getUrl(), null, "image", image.getId(), null);
        return sendResult;
    }

    /**
     * 发送语音消息给用户
     *
     * @param accountId
     * @param openId
     * @param materialId
     * @return
     * @throws Exception
     */
    public boolean saveAndPostVoiceMsg(Integer accountId, String openId, String materialId, String path) throws Exception {
        log.debug("**************************发送语音消息给用户****************************");
        Boolean sendResult = false;
        Account account = accountService.loadAccount(accountId);
        String accessToken = WeixinTimeUtil.getAccessToken(account);
        MaterialVoice voice = materialService.loadMaterialVoice(Integer.parseInt(materialId));
        String url = voice.getUrl();
        String hqUrl = voice.getHqUrl();
        String mediaId = WeixinTimeUtil.getVoiceMediaId(path + "home/wxmanage/upload/" +
                account.getUser().getId() + "/voice/" + (url == null || "".equals(url) ? hqUrl : url), voice);
        String jsonMsg = CustomMsgService.createVoiceMsg(openId, mediaId);
        sendResult = CustomMsgUtil.sendCustomMsg(accessToken, jsonMsg);
        this.saveCustomReply(account, sendResult, openId, (url == null || "".equals(url) ? hqUrl : url), null, "voice", voice.getId(), voice.getName());
        return sendResult;
    }

    /**
     * 发送视频消息给用户
     *
     * @param accountId
     * @param openId
     * @param materialId
     * @return
     * @throws Exception
     */
    public boolean saveAndPostVideoMsg(Integer accountId, String openId, String materialId, String path) throws Exception {
        log.debug("************************发送视频消息给用户******************************");
        Boolean sendResult = false;
        Account account = accountService.loadAccount(accountId);
        String accessToken = WeixinTimeUtil.getAccessToken(account);
        MaterialVideo video = materialService.loadMaterialVideo(Integer.parseInt(materialId));
        String mediaId = WeixinTimeUtil.getVideoMediaId(path + "home/wxmanage/upload/" +
                account.getUser().getId() + "/video/" + video.getUrl(), video);
        String thumbMediaId = WeixinTimeUtil.getThumbImageMediaId(path + "home/wxmanage/upload/" +
                account.getUser().getId() + "/image/" + video.getThumbMedia().getUrl(), video.getThumbMedia());
        String jsonMsg = CustomMsgService.createVideoMsg(openId, mediaId, video.getTitle(), video.getDescription(), thumbMediaId);
        sendResult = CustomMsgUtil.sendCustomMsg(accessToken, jsonMsg);
        this.saveCustomReply(account, sendResult, openId, video.getUrl(), null, "video", video.getId(), video.getName());
        return sendResult;
    }

    /**
     * 发送单图文消息给用户
     *
     * @param accountId
     * @param openId
     * @param materialId
     * @param url
     * @return
     * @throws Exception
     */
    public boolean saveAndPostNewsMsg(Integer accountId, String openId, String materialId, String url) throws Exception {
        log.debug("*************************发送单图文消息给用户*****************************");
        Boolean sendResult = false;
        Account account = accountService.loadAccount(accountId);
        String accessToken = WeixinTimeUtil.getAccessToken(account);
        MaterialNews materialNews = materialService.loadMaterialNews(Integer.parseInt(materialId));
        List<Articles> articleList = new ArrayList<>();
        Articles article = new Articles();
        article.setTitle(materialNews.getTitle());
        article.setDescription(materialNews.getDescription());
        article.setPicurl(url + "/home/wxmanage/upload/" + account.getUser().getId() + "/image/" + materialNews.getThumbMedia().getUrl());
        article.setUrl(materialNews.getContentUrl());
        articleList.add(article);
        String jsonMsg = CustomMsgService.createNewsMsg(openId, articleList);
        sendResult = CustomMsgUtil.sendCustomMsg(accessToken, jsonMsg);

        this.saveCustomReply(account, sendResult, openId, materialNews.getTitle() + "," + materialNews.getThumbMedia().getUrl() + "," + materialId
                , null, "news", materialNews.getId(), materialNews.getName());
        return sendResult;
    }

    /**
     * 发送多图文消息给用户
     *
     * @param accountId
     * @param openId
     * @param materialId
     * @param url
     * @return
     * @throws Exception
     */
    public boolean saveAndPostMultinewsMsg(Integer accountId, String openId, String materialId, String url) throws Exception {
        log.debug("**************************发送多图文消息给用户****************************");
        Boolean sendResult = false;
        Account account = accountService.loadAccount(accountId);
        String accessToken = WeixinTimeUtil.getAccessToken(account);

        List<Articles> articleList = new ArrayList<>();
        MaterialMutinews materialMutinews = materialService.loadMaterialMutinews(Integer.parseInt(materialId));
        String[] ids = materialMutinews.getMultIds().split(",");
        StringBuilder strMaterialId = new StringBuilder();
        for (String id : ids) {
            MaterialNews materialNews = materialService.loadMaterialNews(Integer.parseInt(id));
            strMaterialId.append(",").append(materialNews.getId()).append(":").append(materialNews.getTitle());
            Articles article = new Articles();
            article.setTitle(materialNews.getTitle());
            article.setDescription(materialNews.getDescription());
            article.setPicurl(url + "/home/wxmanage/upload/" + account.getUser().getId() + "/image/" + materialNews.getThumbMedia().getUrl());
            article.setUrl(materialNews.getContentUrl());
            articleList.add(article);
        }

        String jsonMsg = CustomMsgService.createNewsMsg(openId, articleList);
        sendResult = CustomMsgUtil.sendCustomMsg(accessToken, jsonMsg);
        this.saveCustomReply(account, sendResult, openId, articleList.get(0).getPicurl() + strMaterialId.toString()
                , null, "multinews", materialMutinews.getId(), materialMutinews.getName());
        return sendResult;
    }

    /**
     * 获取新消息的数量
     *
     * @param time
     * @param openId
     * @param accountId
     * @return
     * @throws Exception
     */
    public Integer getNewMessageCount(String time, String openId, Integer accountId) throws Exception {
        log.debug("********************************获取新消息的数量*********************************");
        Map<String, Object> params = createParamMap();
        params.put("accountId", accountId);
        params.put("time", time != null ? Long.parseLong(time) : 0l);
        String strQuery = "from WeixinUserMessage t where t.valid = 1 and t.account.id = :accountId and t.createTime > :time";
        if (openId != null && !"".equals(openId)) {
            params.put("openId", openId);
            strQuery += " and t.weixinUser.openId = :openId";
        }
        return weixinUserMessageDAO.getTotalCount(strQuery, params);
    }
}
